---
id: principle
title: vue 响应式原理
---

import CodeIframe from "./CodeIframe.js";

## vue 为什么需要对数据进行监听

[看看尤雨溪对响应式的解释](https://v3.cn.vuejs.org/guide/reactivity.html#%E4%BB%80%E4%B9%88%E6%98%AF%E5%93%8D%E5%BA%94%E6%80%A7)

先看一下这个文件 **1.原生操作 DOM.html** 用原生 JS 来给 DOM 赋值，值是可以的变化的，由最初的 10 改为了 20，  
这里并没有响应式的概念，不需要对数据进行监听，页面上的信息也能改变。  
这就是原生 JS 的写法。

<CodeIframe
  url={
    "https://codesandbox.io/embed/xenodochial-platform-q2m7jn?fontsize=14&hidenavigation=1&theme=dark"
  }
/>

网页往往有很多的 DOM 操作，能不能简化一下 DOM 的操作，当然可以，打开 **2.jquery 操作 DOM.html**的代码。  
引入了 JQ， 简化了操作 DOM 的复杂性,这样写是比原生操作简单了很多，但是基本思想没变。

我们打开 **3.vue 操作 DOM.html**的代码,看一下 vue 2 的写法。

<!-- vue 的写法看上去是更复杂了  
vue 的优点  
1.撇弃了先获取 DOM 再进行赋值的操作，直接操作数据进行操作。  
2.开发的工具变了，利用了 node 进行开发前端的页面。   -->

## vue2 的数据监听原理

靠的是[Object.defineProperty](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)这个方法.

1.** watch.html **文件里我们写了一个简单的监听方法叫做 observer，监听一个叫 data 的对象，它有 a 和 b 两个属性,  
监听到对象里的 a 和 b 的值每次变化，都能知道并做出处理  
（这里处理是用 render 函数模拟 DOM 的变化,在控制台打印出来）

<CodeIframe
  url={
    "https://codesandbox.io/embed/vue004-jf0zum?fontsize=14&hidenavigation=1&theme=dark"
  }
/>

这个时候我们如果给对象加个 c 属性，值也是个对象{xxx:111}, 如果改变这个值，发现是监听不到的。所以我们还需要添加深度监听。  
** 2.deepWatch.html ** 在这里使用了递归来实现深度监听。
发现 对象属性的新增和删除无法监视，所以 vue2 有 vue.set,vue.delete 方法。

** 3.arrWatch.html ** Object.defineProperty 是无法监听数组的，但 vue 中是可以的，它是怎么实现的呢。 

从上面的3个文件我们发现，Object.defineProperty() 在响应式方面的不足。  
如深度监听靠递归，计算量又比较大。监听不到新增和删除的属性。原生监听不了数组等。

## vue3 数据监听原理

[Proxy](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy)  
[Reflect](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect)  
[比较 Reflect 和 Object 方法](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/Comparing_Reflect_and_Object_methods)   
[元编程](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Meta_programming)

<CodeIframe
  url={
    "https://codesandbox.io/embed/vue-proxy-ovo8ur?fontsize=14&hidenavigation=1&theme=dark"
  }
/>

proxy 避开了 Object.defineProperty 的问题。
proxy 兼容性问题，无法 polyfill。


## chartgpt
数据劫持  
Vue通过Object.defineProperty()方法来劫持数据，即在对象的getter和setter方法中加入逻辑，使得在读取或者修改数据时，能够进行一些预处理。这里主要是在setter方法中进行监听数据变化，并通知相关的Watcher对象更新视图。

依赖收集  
当视图中使用到某个响应式数据时，会创建一个Watcher对象，并将该对象加入到一个依赖收集器中。依赖收集器就是一个数组，它存放着所有依赖于响应式数据的Watcher对象。

派发更新  
当响应式数据发生变化时，会触发setter方法中的监听函数，该函数会遍历所有依赖于该数据的Watcher对象，并调用它们的update()方法。这样就能够保证在数据变化时，所有依赖该数据的Watcher都能够及时更新视图。

Watcher  
Watcher是Vue中实现响应式的核心对象之一，它主要用于观察数据变化，并通知相关的视图进行更新。Watcher对象包含三个重要的属性：vm、expOrFn和cb，分别代表Vue实例、依赖的响应式数据以及数据变化时需要执行的回调函数。

综上所述，Vue的响应式实现原理主要是通过数据劫持、依赖收集、派发更新和Watcher对象来实现的。这种实现方式能够让Vue能够高效地实现数据绑定，提高了开发效率和代码的可维护性。
